package debug

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"math/big"
	"kumachan/standalone/util/richtext"
	"kumachan/interpreter/compiler/checker/typsys"
	"kumachan/interpreter/runtime/rt"
	"kumachan/interpreter/runtime/lib/container"
	"kumachan/stdlib"
)


type Value = rt.Value

func Inspect(v Value, t typsys.Type) richtext.Block {
	var vague = func(t_keyword string, v Value) richtext.Block {
		var b richtext.Block
		b.WriteSpan("{", richtext.TAG_SRC_KEYWORD)
		b.WriteSpan(t_keyword, richtext.TAG_SRC_KEYWORD)
		b.WriteSpan(InspectUntypedValue(v), richtext.TAG_SRC_NORMAL)
		b.WriteSpan("}", richtext.TAG_SRC_KEYWORD)
		return b
	}
	var constant = func(t typsys.Type, v Value) richtext.Block {
		var b richtext.Block
		b.WriteSpan("{", richtext.TAG_SRC_KEYWORD)
		b.WriteSpan(typsys.DescribeType(t, nil), richtext.TAG_SRC_TYPE)
		b.WriteSpan(fmt.Sprint(v), richtext.TAG_SRC_NORMAL)
		b.WriteSpan("}", richtext.TAG_SRC_KEYWORD)
		return b
	}
	if t == nil {
		return vague("?", v)
	}
	switch T := t.(type) {
	case *typsys.UnknownType:
		return vague("unknown", v)
	case typsys.UnitType:
		var b richtext.Block
		b.WriteSpan("()", richtext.TAG_SRC_NORMAL)
		return b
	case typsys.TopType:
		return vague("any", v)
	case typsys.BottomType:
		return vague("never", v)
	case typsys.ParameterType:
		return vague("T", v)
	case *typsys.NestedType:
		switch T := T.Content.(type) {
		case typsys.Ref:
			var write_type_line = func(b *richtext.Block) {
				var t_desc = typsys.DescribeType(t, nil)
				b.WriteLine(t_desc, richtext.TAG_SRC_TYPE)
			}
			switch content := T.Def.Content.(type) {
			case *typsys.Box:
				var b richtext.Block
				write_type_line(&b)
				var inner = Inspect(v, content.InflatedInnerType(T))
				b.Append(inner)
				return b
			case *typsys.Enum:
				var enum = v.(rt.EnumValue)
				var b richtext.Block
				write_type_line(&b)
				var case_def = content.CaseTypes[enum.Index]
				var case_t = &typsys.NestedType { Content:
				typsys.Ref {
					Def:  case_def,
					Args: T.Args,
				} }
				var inner = Inspect(enum.Value, case_t)
				b.Append(inner)
				return b
			case *typsys.Native:
				var n = T.Def.Name
				if n.ModuleName == stdlib.Mod_core {
					switch n.ItemName {
					case stdlib.Char:
						var char = v.(rune)
						var char_str = string([] rune { char })
						var char_desc = fmt.Sprintf("`%s` (%d)", char_str, char)
						return constant(t, char_desc)
					case stdlib.String:
						var str = v.(string)
						var str_desc = strconv.Quote(str)
						return constant(t, str_desc)
					case stdlib.Integer:
						return constant(t, v.(*big.Int).String())
					case stdlib.Float, stdlib.Complex:
						return constant(t, v)
					case stdlib.List:
						if reflect.TypeOf(v).Kind() == reflect.Slice {
							v = container.ListFrom(v)
						}
					}
				}
			}
			var b richtext.Block
			write_type_line(&b)
			var inspectable, is_inspectable = v.(rt.Inspectable)
			if is_inspectable {
				b.Append(inspectable.Inspect(Inspect, T.Args))
			} else {
				b.WriteLine(InspectUntypedValue(v), richtext.TAG_SRC_NORMAL)
			}
			return b
		case typsys.Tuple:
			var V = v.(rt.TupleValue)
			var b richtext.Block
			for i, item_type := range T.Elements {
				var child = Inspect(V.Elements[i], item_type)
				b.Append(child)
			}
			return b
		case typsys.Record:
			var V = v.(rt.TupleValue)
			var b richtext.Block
			for i, field := range T.Fields {
				var child richtext.Block
				child.WriteSpan(field.Name, richtext.TAG_SRC_NAME)
				child.WriteSpan(":", richtext.TAG_SRC_NORMAL)
				child.Append(Inspect(V.Elements[i], field.Type))
				b.Append(child)
			}
			return b
		case typsys.Lambda:
			var b richtext.Block
			b.WriteLine(typsys.DescribeType(t, nil), richtext.TAG_SRC_TYPE)
			var child richtext.Block
			child.WriteSpan(InspectUntypedValue(v), richtext.TAG_SRC_NORMAL)
			b.Append(child)
			return b
		default:
			return vague("?", v)
		}
	default:
		return vague("?", v)
	}
}

func InspectUntypedValue(v Value) string {
	switch V := v.(type) {
	case nil:
		return "()"
	case struct{}:
		return "struct{}"
	case string:
		return strconv.Quote(V)
	case reflect.Value:
		return "reflect.Value"
	case *reflect.Value:
		return "*reflect.Value"
	case rt.EnumValue:
		return InspectUntypedValue(V.Value)
	case rt.TupleValue:
		var inner = make([] string, len(V.Elements))
		for i, item := range V.Elements {
			inner[i] = InspectUntypedValue(item)
		}
		return fmt.Sprintf("(%s)", strings.Join(inner, ","))
	case rt.InterfaceValue:
		return "interface"
	case rt.FuncValue:
		switch F := (*V).(type) {
		case rt.UsualFunction:
			var name = F.Entity.Info.Symbol.MessageString()
			var L = len(F.Context)
			if L > 0 {
				return fmt.Sprintf("func[%s]<%d>)", name, L)
			} else {
				return fmt.Sprintf("func[%s]", name)
			}
		default:
			return "func[]"
		}
	default:
		{ var list, is_list = container.MaybeListFrom(v)
		if is_list {
			var inner = make([] string, list.Length())
			list.ForEach(func(i uint, item rt.Value) {
				inner[i] = InspectUntypedValue(item)
			})
			return fmt.Sprintf("[%s]", strings.Join(inner, ","))
		} }
		return fmt.Sprintf("%s(%v)", reflect.TypeOf(v), v)
	}
}


